﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace TuringMachineSimulator
{

    /// <summary>
    /// class TuringTransition represent a single transaction withing a turing machine program
    /// </summary>
    public class TuringTransition
    {

        int _inputState;
        char _input;
        int _outputState;
        char _output;
        char _moveTape;

        /// <summary>
        /// get, set input state for a transition in turing machine program
        /// </summary>
        public int InputState
        {
            get { return _inputState; }
            set { _inputState = value; }
        }

        /// <summary>
        /// get, set input character for a transition in turing machine program
        /// </summary>
        public char Input
        {
            get { return _input; }
            set { _input = value; }
        }

        /// <summary>
        /// get, set Output state for a transition in turing machine program
        /// </summary>
        public int OutputState
        {
            get { return _outputState; }
            set { _outputState = value; }
        }


        /// <summary>
        /// get, set Output character for a transition in turing machine program
        /// </summary>
        public char Output
        {
            get { return _output; }
            set { _output = value; }
        }


        /// <summary>
        /// get, set MoveTape (l-Left, r-Right) for a transition in turing machine program
        /// </summary>
        public char MoveTape
        {
            get { return _moveTape; }
            set { _moveTape = value; }
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="i_s"></param>
        /// <param name="i"></param>
        /// <param name="o_s"></param>
        /// <param name="o"></param>
        /// <param name="m_t"></param>
        public TuringTransition(int i_s, char i, int o_s, char o, char m_t)
        {
            _inputState = i_s;
            _input = i;
            _outputState = o_s;
            _output = o;
            _moveTape = m_t;

        }

        /// <summary>
        /// Constructor
        /// </summary>
        public TuringTransition()
        {
            _inputState = 0;
            _input = '0';
            _outputState = 0;
            _output = '0';
            _moveTape = '0';

        }

        /// <summary>
        /// This method return true if state passes matches the inputState and input character matches the Input. Else it returns false.
        /// </summary>
        /// <param name="i_s"></param>
        /// <param name="i"></param>
        /// <returns></returns>
        public bool findTransition(int i_s, char i)
        {
            if (i_s == InputState && i == Input)
                return true;
            else
                return false;
        }
        /*
              void setdata(int i_s, char i, int o_s, char o, char m_t)
              {
                  input_state = i_s;
                  input = i;
                  output_state = o_s;
                  output = o;
                  move_tape = m_t;
              }
              void getdata(out int i_s, out char i, out int o_s, out char o, out char m_t)
              {
                  i_s = input_state;
                  i = input;
                  o_s = output_state;
                  o = output;
                  m_t = move_tape;
              }
          */
    }


    /// <summary>
    /// Class TuringProgram store the complete program for turing machine.
    /// </summary>
    public class TuringProgram
    {
        List<TuringTransition> turingProgram;

        /// <summary>
        /// Return the the list containing turing machine program
        /// </summary>
        public List<TuringTransition> TuringProgramList
        {
            get { return turingProgram; }
        }

        /// <summary>
        /// Constructor
        /// </summary>
        public TuringProgram()
        {
            turingProgram = new List<TuringTransition>();
        }

        /// <summary>
        /// This method takes the 5 values as input and insert them into turing machine program tuple
        /// </summary>
        /// <param name="input_state"></param>
        /// <param name="input"></param>
        /// <param name="output_state"></param>
        /// <param name="output"></param>
        /// <param name="move_tape"></param>
        public void insertTransition(int input_state, char input, int output_state, char output, char move_tape)
        {
            turingProgram.Add(new TuringTransition(input_state, input, output_state, output, move_tape));
        }

        /// <summary>
        /// This method search through the turing machine program tuples to find a match for a given state and given input
        /// </summary>
        /// <param name="input_state">input parameter</param>
        /// <param name="input">input parameter</param>
        /// <param name="output_state">Output Parameter</param>
        /// <param name="output">Output Parameter</param>
        /// <param name="move_tape">Output Parameter</param>
        /// <returns></returns>
        public bool findTransition(int input_state, char input, out int output_state, out char output, out char move_tape)
        {

            output_state = 0;
            output = '0';
            move_tape = '0';
            if (turingProgram != null)
            {
                foreach (TuringTransition transition in turingProgram)
                {
                    if (transition.findTransition(input_state, input))
                    {
                        output_state = transition.OutputState;
                        output = transition.Output;
                        move_tape = transition.MoveTape;
                        return true;
                    }

                }
            }
            return false;

        }
        /// <summary>
        /// Test method: TBI
        /// </summary>
        void show_content()
        {

        }

    };


    /// <summary>
    /// Class TuringProgramLoader provides methods to load the transitions/turing machine program from file to TuringProgram object.
    /// This class extends the TuringProgram class
    /// </summary>
    public class TuringProgramLoader : TuringProgram
    {
        public TuringProgramLoader()
        {

        }

        /// <summary>
        /// Read turing machine program from the file. 
        /// </summary>
        /// <param name="fileName">Full path of the file which contains the turing machine program</param>
        public void readTransitions(string fileName)
        {
            if (fileName == null)
            {
                throw new MissingFieldException("fileName is empty");
            }

            int index = 0;
            try
            {

                FileStream fs1 = File.OpenRead(fileName);
                StreamReader reader;
                reader = new StreamReader(fs1);

                string str = string.Empty;
                str = reader.ReadLine();
                while (!reader.EndOfStream && str.Length < 1)
                {
                    str = reader.ReadLine();
                    index++;
                }

                while (index++ < 10000) //Putting an upper limit on number of transitions to be read
                {

                    if (!string.IsNullOrEmpty(str))
                    {
                        int input_state;
                        char input;
                        int output_state;
                        char output;
                        char move_tape;

                        // transition format should be "1 1 2 x r"
                        string[] temparr = str.Split(' ');
                        input_state = Int32.Parse(temparr[0]);
                        input = char.Parse(temparr[1]);
                        output_state = Int32.Parse(temparr[2]);
                        output = char.Parse(temparr[3]);
                        move_tape = char.Parse(temparr[4]);

                        insertTransition(input_state, input, output_state, output, move_tape); // calling parent class method

                        if (reader.EndOfStream)
                        { break; }
                        str = reader.ReadLine();
                    }

                }
            }
            catch (Exception ex)
            {
                throw new FormatException(string.Format("Error reading values at index: %d in input transition file: %s. Exception is: %s. Inner exception: %s ", index, fileName, ex.Message, ex.InnerException));

            }
        }
    }

}
